package org.NooLab.glue.net.http.rest;



import java.net.BindException;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.restlet.Application;
import org.restlet.Component;
import org.restlet.Context;
import org.restlet.Restlet;
import org.restlet.Server;
import org.restlet.data.ChallengeScheme;
import org.restlet.data.Protocol;
 

import org.NooLab.glue.components.DataReceiverIntf;
import org.NooLab.glue.net.http.rest.resources.DataResource;
import org.NooLab.glue.net.http.rest.resources.RegistrationsResource;
import org.NooLab.glue.net.http.rest.resources.ResourceBindingForBoards;
import org.NooLab.glue.net.http.rest.resources.ResourceBindingForClients;
import org.NooLab.glue.net.http.rest.resources.ResourceBindingForReceptors;
import org.NooLab.glue.net.http.rest.resources.ResourceBindingForSources;
import org.NooLab.glue.net.http.rest.resources.StateResource;
import org.NooLab.utilities.logging.CreateLogging;
import org.NooLab.utilities.logging.LogControl;
import org.NooLab.utilities.logging.PrintLog;

import org.restlet.routing.Router;
import org.restlet.routing.Template;
import org.restlet.security.ChallengeAuthenticator;
import org.restlet.security.MapVerifier;
import org.restlet.util.RouteList;
 


/**
 * 
 *  a call for a simple get to VMware win2K is :  http://192.168.46.131:6100/ 
 * 
 */
 
/*
 
    System.setProperty("java.util.logging.config.file", "/your/path/logging.config"); 
        
        
 	authentication is required!
 	
 	put  	->  for opening a transaction, or registering
 	post	->  for transferring data, polls etc.
 	get 	->  for state requests about transactions
 
 
  	Server httpServer = new Server(Protocol.HTTP, port);
	serviceComponent.get​Servers().add(httpSe​rver);
	httpServer.getContex​t().getParameters().​add("maxThreads", maxThreads);
	
 	You can set the pool size of the executor used by the TaskService with
	org.restlet.service.​TaskService.setPoolS​ize.

	Or you can provide your own TaskService and override
	createExecutorService.to return an ExecutorService tuned exactly the way you

	check this out, could be interesting:
	
	riap://component/internalpath/Y
	riap == Restlet Internal Access Protocol
	
	
	
	
 */
//===============================================================================
//===============================================================================
//===============================================================================
 
 

public class HttpRestletServer 
								extends 
											Observable
								implements 
											DataReceiverIntf,
											RestletMessageSinkIntf	{ // , 

	RestletBoxPropertiesIntf restletBoxProperties ;
	ResourceBindingForSources bindingForSources ;
	ResourceBindingForReceptors bindingForReceptors;
	ResourceBindingForBoards bindingForBoards ;
	
	public Component component ;
	public Observer observingMsgBoard ;
 
	Router router;

	int connectionResponseTimeoutValue = 5000 ;
	
	int status = -1;
	
	PrintLog out = new PrintLog(2,true);
	
	public HttpRestletServer( RestletBoxPropertiesIntf restletBoxProps, Observer observer){
		
		restletBoxProperties = restletBoxProps;
		
		observingMsgBoard = observer ;
		
		createServer();  
		 
	}

	
	public void createServer(){
		
		int port, sourcesPort , receptorsPort , iboardPort; 
		
	 
		
		/* 
		Logger logger = Logger.getLogger("org.restlet");
	    for (Handler handler : logger.getParent().getHandlers()) {
	        // Find the console handler
	        if (handler.getClass().equals(java.util.logging.ConsoleHandler.class)) {
	            // set level to SEVERE. We could disable it completely with 
	            // a custom filter but this is good enough.
	            handler.setLevel(Level.SEVERE);
	        }
	    }
	    */
	     
												out.print(4, "creating HttpRestetServer...");
		port = restletBoxProperties.getListeningPort() ; // in case of the server this is a real=physical port!
		sourcesPort = restletBoxProperties.getPortAliasForSources() ;
		receptorsPort = restletBoxProperties.getPortAliasForReceptors() ;
		iboardPort = restletBoxProperties.getPortAliasForIBoard() ;
		
		
		// Create a new Component.
        component = new Component();

        
        // Add a new HTTP server listening on port .
        component.getServers().add(Protocol.HTTP, port);

        Server server = component.getServers().get(0) ;
        Context ctx =  server.getContext();
        ctx.getParameters().add("maxThreads​", "510");
        // ConcurrentMap<String, Object> attr = ctx.getAttributes();
        										out.print(5, "HttpRestetServer: binding resources...");
        bindingForSources = new ResourceBindingForSources( this, restletBoxProperties) ;
        bindingForReceptors = new ResourceBindingForReceptors(this, restletBoxProperties) ;
        bindingForBoards = new ResourceBindingForBoards(this, restletBoxProperties) ;
        										out.print(5, "HttpRestetServer: defining routes...");
        // these are routes for forking the incoming requests
        component.getDefaultHost().attach("/vp"+sourcesPort , bindingForSources );
        component.getDefaultHost().attach("/vp"+receptorsPort , bindingForReceptors);
        component.getDefaultHost().attach("/vp"+iboardPort  , bindingForBoards);
        // and for Boards ...
        
        // router.attachDefault( SimpleGlueMessage.class );  default route for non-matches

        // just attaching the Application to the internal router enables for the RIAP !!
        component.getInternalRouter().attachDefault(bindingForSources);
        component.getInternalRouter().attachDefault(bindingForReceptors);
        component.getInternalRouter().attachDefault(bindingForBoards);
        
        /*   then it is possible to do something like this
          
         	Accessing resources of the application through RIAP is then simple since you can use the Restlet client support as with other protocols:

			Request request = new Request(Method.GET, "riap://component/ping");
			Response response = getContext().getClientDispatcher().handle(request);
			Representation repr = response.getEntity();

          	context.getClientDispatcher().get("riap://component/some-path/Y");
          
         */
        
        
        // Start the component.
        try {
        										out.print(5, "HttpRestetServer: starting...");
        	component.start();
        	 
        	status = 0;

        	// needs classloader and package path, yet, it does not exist before invocation on physical receive
        	// Class<?> clazz = Class.forName("RegistrationsResource");
    			
        	// RouteList routes = component.getDefaultHost().getRoutes() ;
        } catch(BindException bex){
        	String msgstr = bex.getMessage() ;
        	
        	msgstr = "HttpRestetServer: start failed, unable to make the server listen to port "+port+", reason: "+ msgstr;
        	out.printErr(1, msgstr) ;
        	status = -5;
        	
        } catch (Exception e) {
        	status = -11;
			e.printStackTrace();
		}
	}

	

	// ------------------------------------------------------------------------

	public Router getRouter() {
		return router;
	}


	// RestletMessageSink

	/*
	 * 
	 * the problem with all this is, that the resource handlers like "RegistrationResource" are
	 * created as well-separated thread, and here they are collated into a single thread...
	 * 
	 * thus, the import of a resultsObserver (also called from the freshly created handler object)
	 * must itself trigger the creation of a handler thread, which then is actually observing=waiting for
	 * a notification
	 * 
	 */
 
	// contracted through interface RestletMessageSinkIntf, called from the request handlers, 
	// e.g. "RegistrationResource", method "prepareMessageRouting()"
	@Override
	public void importResultsObserver( Observer resultsObserver ) {
	/*
	 * the problem is that fixing the observer here and the data delivery through "returningResultsForResponse()" (next method)
	 * as called from class "HttpRestletInternalSender{}" will take some time, so request that follow quickly will overlap,
	 * resulting in a crash of the session
	 * 
	 * that means, that we have to open a new observer for each incoming observation request
	 * namely the field  "resultsObserver" should not be global for the HttpServer	
	 */
		 
		
	// without threading upon import, we have to schedule them into single rails...
	// the observer is actualized by the class "MessageSupply{}"
		  
		// create a threaded class here !!! and let this be the observer
		addObserver(resultsObserver);
		
												// outx out.print(4, "Observer added : "+ resultsObserver.toString() );
		// now we can notifyObserver ->  MessageSupply
		
		// somehow it seems, that we need explicit callbacks ... which we can manage better
	}

	@Override
	public void removeResultsObserver(Observer observer) {
		
		// just to be sure, and avoiding overload of garbage collection
		deleteObserver(observer) ;
		
	}

	/**
	 * 
	 * this is called from the object "HttpRestletInternalSender{}"
	 * it is not a physical sending, just a logical through the observer pattern
	 * the "sending" is actually a in-session response !
	 * 
	 * @param msg
	 */
	public void returningResultsForResponse( String msg){
		// notification will inform MessageSupply, which is part of RegistrationResource
		int cob;
		cob = countObservers();
		
		if (cob>0){
			cob=cob+0;
		}
		// outx out.print(4, "HttpRestletServer, returningResultsForResponse(): relaying outgoing message from HttpRestletInternalSender to "+cob+" observers " ) ;
		out.print(6, "                   message is \n"+ 
					 "                   "+msg); 
		
		
		setChanged();  
		this.notifyObservers(msg); // the messages need a UUID!
		// this jumps back to msgSupply{} in the request handler
		
	}

	public Observer getObservingMsgBoard() {
		return observingMsgBoard;
	}
	
	// ------------------------------------------------------------------------

	@Override
	public void setObserver(Observer dataConnector) {
		// TODO 
		
	}


	@Override
	public void deleteObservers() {
		 
		
	}
	
	@Override
	public int getListenPort() {
		 
		return 0;
	}


	@Override
	public void closePort() {
		 
		
	}


	public RestletBoxPropertiesIntf getRestletBoxProperties() {
		return restletBoxProperties;
	}


	public ResourceBindingForSources getBindingForSources() {
		return bindingForSources;
	}


	public ResourceBindingForReceptors getBindingForReceptors() {
		return bindingForReceptors;
	}


	@Override
	public int getTimeoutValue() {
		 
		return connectionResponseTimeoutValue;
	}


	public int getConnectionResponseTimeoutValue() {
		return connectionResponseTimeoutValue;
	}


	public void setConnectionResponseTimeoutValue(int connectionResponseTimeoutValue) {
		this.connectionResponseTimeoutValue = connectionResponseTimeoutValue;
	}
 

}

//===============================================================================

 
 